package cn.dengta.webapp.trade.service;

import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.*;

import cn.dengta.common.model.Bean;
import cn.dengta.common.util.Formats;
import cn.dengta.webapp.base.service.BaseServiceImpl;
import cn.dengta.webapp.trade.dao.UserEquityBagDao;
import cn.dengta.webapp.trade.dao.UserEquityBagLogDao;
import cn.dengta.webapp.trade.entity.*;
import cn.dengta.webapp.trade.model.UserEquityType;
import cn.dengta.webapp.user.model.UserVipRule;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import me.codeplayer.util.*;
import org.apache.commons.lang3.time.FastDateFormat;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import static cn.dengta.webapp.trade.entity.UserEquityBag.*;

/**
 * 用户权益包 服务实现类
 *
 * @author xy
 * @since 2022-08-04
 */
@Service
@Slf4j
public class UserEquityBagService extends BaseServiceImpl<UserEquityBag, UserEquityBagDao, Long> {

	@Resource
	UserVipEquityService userVipEquityService;
	@Resource
	UserBagAvatarService userBagAvatarService;
	@Resource
	RedisTemplate<String, Object> redisTemplate;
	@Resource
	UserEquityBagLogDao userEquityBagLogDao;

	public static final String USER_AVATAR_BOX = "user:avatar:box";

	public static final EasyDate last_date = new EasyDate(2099, 12, 31).endOf(Calendar.DAY_OF_MONTH);

	public static final int last_day = 360 * 100;

	/**
	 * 查询可用的权益列表
	 */
	public Collection<UserEquityBag> findByUserAndType(Long userId, UserEquityType type) {
		List<UserEquityBag> list = baseDao.findByUserAndType(userId, type.value);
		if (X.isValid(list)) {
			if (type == UserEquityType.GIFT || type == UserEquityType.AVATAR) {
				return list;
			}
			Map<Date, List<UserEquityBag>> map = list.stream().collect(Collectors.groupingBy(UserEquityBag::getExpireTime));
			TreeSet<UserEquityBag> bags = new TreeSet<>(Comparator.comparing(UserEquityBag::getExpireTime));
			for (Map.Entry<Date, List<UserEquityBag>> entry : map.entrySet()) {
				UserEquityBag reduce = entry.getValue().stream().reduce(null, (i, n) -> {
					if (i == null) {
						i = n;
						i.setAddTime(null);
						i.setUpdateTime(null);
						i.setPeriod(null);
					} else {
						i.setTotal(i.getTotal() + n.getTotal());
						i.setCount(i.getCount() + n.getCount());
						i.setRemain(i.getRemain() + n.getRemain());
					}
					return i;
				});
				bags.add(reduce);
			}
			return bags;
		}
		return null;
	}

	/**
	 * 查询第一个可用的权益
	 */
	public UserEquityBag findFirstByUserAndType(Long userId, UserEquityType type) {
		return this.getBaseDao().findFirstByUserAndType(userId, type.value);
	}

	/**
	 * 获取有效剩余通话时间
	 */
	public int getTotalRemainByType(Long userId, UserEquityType type, Date startTime) {
		return NumberUtil.getInt(baseDao.getTotalRemainByType(userId, type.getValue(), startTime), 0);
	}

	/**
	 * 获取有效音视频卡列表
	 * 【注意】该方法只会返回 id 和 remain 字段
	 */
	public List<UserEquityBag> findRemainByType(Long userId, UserEquityType type, Date startTime) {
		return baseDao.findRemainByType(userId, type.getValue(), startTime);
	}

	/**
	 * VIP赠送（音频、视频）
	 */
	@Transactional(propagation = Propagation.NESTED)
	public void giveAway(Long userId, Date vipRechargeTime, Long vipConfigId) {
		final long now = System.currentTimeMillis();
		if (vipRechargeTime == null || vipRechargeTime.getTime() > now) {
			return;
		}
		final EasyDate d = new EasyDate(now).endOf(Calendar.DAY_OF_MONTH);
		int days = (int) ((d.getTime() - vipRechargeTime.getTime()) / EasyDate.MILLIS_OF_DAY);
		boolean check = days % 30 == 0;
		if (check) {
			String period = d.setTime(now).toString(Formats.getDateFormat(EasyDate.YM_DATE));
			UserVipRule rule = userVipEquityService.getCached(vipConfigId);
			log.info("用户 {} 权益数据: {}", userId, JSONUtil.encode(rule));
			FastDateFormat format = Formats.getDateFormat(Formats.formatDate_YMD);
			Integer videoDayLife = rule.getVideoDayLife();
			if (Cmp.gt(videoDayLife, 0)) {
				// 当天需要 算一天
				Date videoExpireTime = d.setTime(now).addDay(rule.getVideoDayLife() - 1).endOf(Calendar.DAY_OF_MONTH).toDate();
				UserEquityBag bag = of(userId, period + "_" + d.toString(format), UserEquityType.VIDEO, videoExpireTime, rule.getVideoMinute());
				this.save(bag);
				saveLog(userId, UserEquityType.VIDEO, videoDayLife, "VIP", bag.getId());
			}

			// 当天需要 算一天
			Integer audioDayLife = rule.getAudioDayLife();
			if (Cmp.gt(audioDayLife, 0)) {
				Date audioExpireTime = d.setTime(now).addDay(rule.getAudioDayLife() - 1).endOf(Calendar.DAY_OF_MONTH).toDate();
				UserEquityBag bag = of(userId, period + "_" + d.toString(format), UserEquityType.AUDIO, audioExpireTime, rule.getAudioMinute());
				this.save(bag);
				saveLog(userId, UserEquityType.AUDIO, audioDayLife, "VIP", bag.getId());
			}
		}
	}

	private void saveLog(Long userId, UserEquityType type, int total, String buss, Long bagId) {
		UserEquityBagLog log = new UserEquityBagLog();
		log.setUserId(userId);
		log.setType(type);
		log.setTotal(total);
		log.setBagId(bagId);
		log.setBuss(buss);
		log.initTime(new Date());
		userEquityBagLogDao.insert(log);
	}

	/** 活动 赠送音视频卡 */
	public void giveCardBuss(UserEquityType type, int minute, int day, @Nullable String buss, @Nullable String uniqueKey, Long... userIds) {
		giveBagEquity(type, null, null, minute, day, uniqueKey, buss, userIds);
	}

	/** 活动 赠送音视频卡 */
	public void giveCard(UserEquityType type, int minute, int day, Long... userIds) {
		giveCardBuss(type, minute, day, null, null, userIds);
	}

	/** 活动 赠送音视频卡 */
	public void giveCardBuss(UserEquityType type, int minute, String buss, Long... userIds) {
		giveBagEquity(type, null, null, minute, null, null, buss, userIds);
	}

	/** 活动 赠送音视频卡 */
	public void giveCard(UserEquityType type, int minute, Long... userIds) {
		giveCardBuss(type, minute, "", userIds);
	}

	private void giveBagEquity(UserEquityType type, Long giftId, String giftName, @Nonnull Integer total, @Nullable Integer day, @Nullable String uniqueKey, String buss, Long... userIds) {
		final EasyDate d = new EasyDate();
		String beg = d.toShortString();
		// 当天需要 算一天
		Date expireTime = day == null || day == last_day ? last_date.toDate() : d.addDay(day - 1).endOf(Calendar.DAY_OF_MONTH).toDate();
		final String uqKey = StringUtil.notEmpty(uniqueKey) ? uniqueKey : beg + "_" + d.toShortString();
		UserEquityBag entity = of(null, uqKey, type, expireTime, total);
		if (type == UserEquityType.GIFT && Bean.isValidId(giftId)) {
			entity.setGiftId(giftId).setGiftName(giftName);
		}
		for (Long userId : userIds) {
			UserEquityBag bag = findByUniqueKey(type, userId, uqKey);
			if (bag != null) {
				if (type == UserEquityType.AVATAR) {
					continue;
				}
				this.getBaseDao().addTotal(bag.getId(), total, expireTime);
				saveLog(userId, type, total, buss, bag.getId());
			} else {
				entity.setUserId(userId);
				this.save(entity);
				saveLog(userId, type, total, buss, entity.getId());
				entity.setId(null);
			}
		}
	}

	private UserEquityBag findByUniqueKey(UserEquityType type, Long userId, String uniqueKey) {
		UserEquityBag bag = new UserEquityBag();
		bag.setType(type);
		bag.setPeriod(uniqueKey);
		bag.setUserId(userId);
		return selectOne(new QueryWrapper<>(bag));
	}

	/** 活动 礼物 */
	public void giveGift(Long giftId, String giftName, Integer total, Integer day, Long... userIds) {
		giveGiftBuss(giftId, giftName, total, day, "", userIds);
	}

	/** 活动 礼物 */
	public void giveGiftBuss(Long giftId, String giftName, Integer total, Integer day, String buss, Long... userIds) {
		giveBagEquity(UserEquityType.GIFT, giftId, giftName, total, day, giftId.toString(), buss, userIds);
	}

	/** 头像框 */
	public void giveAvatarBuss(String avatarId, String buss, Long... userIds) {
		giveBagEquity(UserEquityType.AVATAR, null, null, 1, null, avatarId, buss, userIds);
	}

	/** 头像框 */
	public void giveAvatar(String avatarId, Long... userIds) {
		giveAvatarBuss(avatarId, "", userIds);
	}

	public int reduceRemain(Long id, Integer used) {
		return baseDao.reduceRemain(id, used);
	}

	public int updateByTypeAndId(Long id, Long userId, Integer count) {
		return getBaseDao().updateByTypeAndId(id, userId, count);
	}

	public String findMasterAvatar(Long userId) {
		UserEquityBag one = findAvatarBag(userId);
		if (one != null) {
			UserBagAvatar avatar = userBagAvatarService.get(Long.parseLong(one.getPeriod()));
			return avatar == null ? null : avatar.getIcon();
		}
		return null;
	}

	private UserEquityBag findAvatarBag(Long userId) {
		QueryWrapper<UserEquityBag> wrapper = new QueryWrapper<UserEquityBag>()
				.eq(USER_ID, userId)
				.eq(TYPE, UserEquityType.AVATAR.value)
				.eq(MASTER, 1) // 默认头像框
				.last("LIMIT 1");
		return this.selectOne(wrapper);
	}

	public Map<Long, String> findMasterAvatarsMap(Long... userIds) {
		if (!X.isValid(userIds)) {
			return Collections.emptyMap();
		}
		String[] us = new String[userIds.length];
		for (int i = 0; i < userIds.length; i++) {
			us[i] = userIds[i].toString();
		}
		List<Long> list = redisTemplate.<String, Long>opsForHash().multiGet(USER_AVATAR_BOX, Arrays.asList(us));
		Map<Long, String> avatarMap = userBagAvatarService.findAvatars();
		Map<Long, String> resultMap = new HashMap<>(userIds.length);
		for (int i = 0; i < list.size(); i++) {
			if (list.get(i) != null) {
				resultMap.put(userIds[i], avatarMap.get(list.get(i)));
			}
		}
		return resultMap;
	}

	/** 同步所有头像框到缓存中 */
	public void flushCacheAvatarAll() {
		redisTemplate.delete(USER_AVATAR_BOX);
		final int limit = 500;
		Long maxId = 0L;
		int size;
		// 默认头像框
		do {
			QueryWrapper<UserEquityBag> queryWrapper = new QueryWrapper<UserEquityBag>()
					.select(ID, USER_ID, PERIOD)
					.eq(TYPE, UserEquityType.AVATAR.value)
					.eq(MASTER, 1)
					.gt(ID, maxId)
					.orderByAsc(ID)
					.last("LIMIT " + limit)
					.orderByAsc(ID);
			List<UserEquityBag> bags = this.selectList(queryWrapper);
			size = bags.size();
			if (size == 0) {
				return;
			}
			Map<String, Long> userIds = CollectionUtil.newHashMap(size);
			for (UserEquityBag bag : bags) {
				userIds.put(bag.getUserId().toString(), Long.parseLong(bag.getPeriod()));
			}
			maxId = bags.get(size - 1).getId();
			flushCacheAvatar(userIds);
		} while (size == limit);
	}

	/** 刷新缓存 */
	public void flushCacheAvatar(Map<String, Long> map) {
		redisTemplate.opsForHash().putAll(USER_AVATAR_BOX, map);
	}

	/** 取消默认头像 */
	public int cancelMasterAvatar(Long userId) {
		UpdateWrapper<UserEquityBag> updateWrapper = new UpdateWrapper<UserEquityBag>()
				.set(MASTER, 0)
				.eq(USER_ID, userId)
				.eq(TYPE, UserEquityType.AVATAR.value)
				.eq(MASTER, 1); // 默认头像框
		return this.update(updateWrapper);
	}

	/** 切换头像边框 */
	public void tabFrame(Long bagId, Long userid, boolean master) {
		if (master) {
			cancelMasterAvatar(userid);
			UserEquityBag bag = get(bagId);
			this.flushCacheAvatar(Map.of(userid.toString(), Long.parseLong(bag.getPeriod())));
		} else {
			redisTemplate.opsForHash().delete(USER_AVATAR_BOX, userid.toString());
		}
		UpdateWrapper<UserEquityBag> updateWrapper = new UpdateWrapper<UserEquityBag>()
				.set(MASTER, master ? 1 : 0)
				.eq(ID, bagId);
		this.update(updateWrapper);
	}

	public List<UserEquityBag> findAvatarByAlias(Long userId, String alias) {
		QueryWrapper<UserEquityBag> wrapper = new QueryWrapper<>();
		final String prefix = "log.";
		wrapper.eq(prefix + USER_ID, userId)
				.eq(prefix + TYPE, UserEquityType.AVATAR.value)
				.eq(StringUtil.notEmpty(alias), "a.alias", alias);
		wrapper.orderByAsc("a.sort", prefix + ID);
		return baseDao.findByAlias(wrapper);
	}

}
